package objects;

import java.io.Serializable;
import java.util.*;

import communication.CurrentCommunication;
import communication.WishedEmission;
import kernel.Simulator;
import results.Traceable;
import communication.thirdLayer.*;


/**
 * @version 1.0 (25 févr. 2004)
 * @author fsemiond
 * @author alemaitr
 * 
 * @version 02-03-2007 
 * @author maubry
 * @author adegomme
 * rajout de la gestion des plages de fréquences dans le protocole CSMA/CA
 * @author bbrison
 * rajout de méthode pour gérer le router de la station
 */
public abstract class Station extends SimObject  implements Serializable,Traceable{

    // Les attributs.
    private double x,y; 			  // Position de la station
    private double reachMaxLength; 	  // Distance de portée
    private double carrierRate; 	  // Taux porteuse/portée
    private double reachRate;		  // Taux portée réelle/portée

    private int stateNbListening;				  // Etat du mobile est donnée par les fonctions
    private boolean stateEmiting;				//getState
    private boolean stateAsleep;
    private CurrentCommunication ccEmiting;	//permet de savoir quel CurrentCommunication a pris l'emission de ce mobile

    private boolean recentlyModified; // Dit si le mobile a été modifié depuis la dernière mise à jour de l'IHM 
    protected Simulator simulator;	  // Le simulateur qui possède la station.

    private Object usedByProtocol; // un champ Objet qui peut etre utilisé par un Protocole
						
    private LinkedList oldInterferenced;	      // Old Jamming frequencies list
    private LinkedList newInterferenced;	      // New Jamming frequencies list	
    private String name;
    private String _xmlKey;

    // Attributs liés à la communication
    private Vector<WishedEmission> wishedEmission;		// liste des souhaits de communication à établir
    public Vector currentCommunication;// liste des communications courantes
    private Vector endedEmission;		// liste des communication terminées (on stocke des current communication) 
    private Vector currentReachNeighbors;//liste des voisins de portée
    private Vector formerReachNeighbors;// liste des anciens voisins de portée
    private Vector currentCarrierNeighbors;//liste des voisins de porteuse
    private Vector formerCarrierNeighbors;//liste des anciens voisins de porteuse
    private boolean recentNeighborhood; //boolean qui indique si on a deja calculé le neighborhood dans ce tour de boucle
    private boolean calcul; // indique si on peut calculler la portée sur la Station
    //liste des probes en string pour les résultats
    protected Vector probes;
    public Vector getProbes(){ return probes; }

    protected static int cptprobes = 0;
    // Définition des états.
    public static final int WAITING = 0;
    public static final int LISTENING = 1;
    public static final int SPEAKING = 2;
    public static final int SLEEPING = 3;
    public static final int ASLEEP = 4; // When the station has no more battery
    public static final int NBSTATES 	= 5;

    private Router router;
    private Vector<WishedEmission3> wishedEmission3;// wished emission at the Third Level
    private int frameId; 

    private float _fmin; // minimum frequency
    private float _fmax; // maximum frequency
    private TreeSet _frange; // set of frequencies



    /************************* Constructeur ************************/

    //constructeur utilisé uniquement pour les tests
    public Station(String s){
	name = s;
	router = new RouterAODV(this);
	_xmlKey = s;
	currentReachNeighbors = new Vector();
	this.frameId = 0; //identification de la trame	
	
    }

    public Station(Simulator s){
	wishedEmission=new Vector();
	wishedEmission3 = new Vector();	

	currentCommunication=new Vector();
	endedEmission= new Vector();
	oldInterferenced=new LinkedList();
	newInterferenced=new LinkedList();
	recentNeighborhood=false;	
	reachRate = 1;
	stateEmiting=false;
	stateAsleep=false;
	stateNbListening=0;  
	probes = new Vector();
	currentReachNeighbors=new Vector();
	formerReachNeighbors=new Vector();
	currentCarrierNeighbors=new Vector();
	formerCarrierNeighbors=new Vector();

	this.frameId=0;
	
	carrierRate = 1;
	// on initialise l'objet Simulator dans Station
	this.simulator = s;

	//initialisation of the frequencies 
	_fmin=0;
	_fmax=0;
	_frange=new TreeSet();
	
	//si ad hoc alors initialise router, sinon ne faire rien
	if(this.simulator.getMode()==Simulator.ADHOC){
	    router=new RouterAODV(this);	
		
		
	}

	
	
    }

    /************************* Méthodes d'accès ********************/

    public Router getRouter(){
	return router;
    }

    public Vector<WishedEmission3> getWishedEmission3(){
	return wishedEmission3;
    }

    public int getFrameId(){
	return frameId;
    }

    public void incrementFrameId(){
	this.frameId++;
    }

    public void setUsedByProtocol(Object obj){
	usedByProtocol=obj;
    }

    public Object getUsedByProtocol(){
	return usedByProtocol;
    }

    public boolean getAsleep(){
	return stateAsleep;
    }

    public void setccEmiting(CurrentCommunication cc){
	ccEmiting=cc;
    }
    public CurrentCommunication getccEmiting(){
	return ccEmiting;
    }

    /**
     * 
     * @param _x the new horizontale coordinate of the station
     */
    public void setX(double _x){x=_x;}

    /**
     * 
     * @param _y the new verticale coordinate of the station
     */
    public void setY(double _y){y=_y;}

    /**
     * 
     * @param name the name of the station
     */
    public void setName(String name){this.name = name;}


    /**
     * @param the router to be set
     */
    public void setRouter(Router r) {
	router = r;
    }

    /**
     * 
     * @param newState the new state of the station
     */
    public void setStateEmiting(boolean state){
	if (stateEmiting==state){
	    System.err.println("erreur dans setStateEmiting");
	}
	stateEmiting=state;	
    }
    public void setStateAsleep(boolean asleep){
	stateAsleep=asleep;
    }
    public void addStateListening(){
	stateNbListening++;
    }
    public void minusStateListening(){
	stateNbListening--;
    }
    /**
     * 
     * @param newMaxLength the new reach max length of the station
     */
    public void setReachMaxLength(double newMaxLength){reachMaxLength=newMaxLength;}

    /**
     * 
     * @param newRate the new carrier rate of the station
     */
    public void setCarrierRate(double newRate){carrierRate=newRate;}

    /**
     * 
     * @param newRate the new reach rate of the station
     */
    public void setReachRate(double newRate){
	reachRate = newRate;
    }

    /**
     * 
     * @param newValue true if the station has been recently modified
     */
    public void setRecentlyModified(boolean newValue){recentlyModified=newValue;}

    /**
     * 
     * @param s the simulator that owns the station.
     */
    protected void setSimulator(Simulator s){simulator=s;}

    /**
     * Add a new station that interference the current station
     *
     */
    public void addInterferenced(float f){
	
	newInterferenced.add(f);
    }

    /**
     * Remove a station that was interfering the current station
     *
     */
    public void removeInterferenced(float f){
	newInterferenced.remove(f);
    }






    public void setRecentNeighborhood(boolean v){
	recentNeighborhood=v;
    }
    public boolean getRecentNeighborhood(){
	return recentNeighborhood;
    }

    /**
     * 
     * @param v the new vector of reach neighbors
     */
    public void setCurrentReachNeighbors(Vector v){
	currentReachNeighbors=v;
    }

    /**
     * 
     * @param v the new vector of carrier neighbors
     */
    public void setCurrentCarrierNeighbors(Vector v){
	currentCarrierNeighbors=v;
    }

    /**
     * 
     * @return the horizontal position of the station.
     */
    public String getName(){return name;}


    /**
     * 
     * @return the horizontal position of the station.
     */
    public double getX(){return x;}

    /**
     * 
     * @return the vertical position of the station.
     */
    public double getY(){return y;}

    /**
     * 
     * @return the maximum length a station can reach by communicating.
     */
    public double getReachMaxLength(){return reachMaxLength;}

    /**
     * 
     * @return the rate between the max length and the actual length a station can reach by communicating.
     */
    public double getReachRate(){return reachRate;}

    /**
     * 
     * @return the reach
     */
    public int getReach(){return (new Double(this.reachMaxLength*reachRate)).intValue();}



    /**
     * 
     * @return the rate between maximum length a station can reach by communicating and the length of its carrier.
     */
    public double getCarrierRate(){return carrierRate;}

    /**
     * 
     * @return the state of the station.
     */
    public int getState(){
	int state;
	if(stateEmiting){
	    state=SPEAKING;
	}
	else{
	    if(stateNbListening>0){
		state=LISTENING;
	    }else{
		if(stateAsleep){
		    state=ASLEEP;
		}else{
		    state=WAITING;
		}
	    }
	}
	return state;
    }

    /**
     * 
     * @return whether the station station has been modified since the last time it's been printed.
     */
    public boolean getRecentlyModified(){return recentlyModified;}

    /**
     * 
     * @return the  number of stations that are interfering with the station
     */
    public LinkedList getInterferenced(){
	return oldInterferenced;
    }
    /**
     * 
     * @return the vector of the mobiles that are reachable
     */
    public Vector getCurrentReachNeighbors(){
	return currentReachNeighbors;
    }

    /**
     * 
     * @return the vector of the mobiles that are carriable
     */
    public Vector getCurrentCarrierNeighbors(){
	return currentCarrierNeighbors;
    }
    /**
     * 
     * @return the former vector of the mobiles that are reachable
     */
    public Vector getFormerReachNeighbors(){
	return formerReachNeighbors;
    }

    /**
     * 
     * @return the former vector of the mobiles that are carriable
     */
    public Vector getFormerCarrierNeighbors(){
	return formerCarrierNeighbors;
    }


    /**
     * 
     * @return the wished Emission of the station
     */
    public Vector<WishedEmission> getWishedEmission(){
	return wishedEmission;
    }

    /**
     * 
     * @return the current communications of the station
     */
    public Vector getCurrentCommunication(){
	return currentCommunication;
    }

    /**
     * 
     * @return the emission that are ended for the station
     */
    public Vector getEndedEmission(){
	return endedEmission;
    }


    /**
     *
     * @return the minimum frequency for communications
     */
    public float getFmin(){
	return _fmin;
    }

    /**
     *
     * @return the maximum frequency for communications
     */
    public float getFmax(){
	return _fmax;
    }

    /**
     *
     * @param the minimum frequency
     */
    public void setFmin(float f){
	_fmin=f;
    }

    /**
     *
     * @param the maximum frequency
     */
    public void setFmax(float f){
	_fmax=f;
    }

    /* method which calculates the frequencies used by the stations in order to communicate
     * @param min : minimal frequency, note: this frequency will be in the set
     * @param freq : maximum frequency, note :
	*/
    public static TreeSet frangeCalc(float min, float max, int number){

	TreeSet resul= new TreeSet();
		resul.add(min);
	if(number!=1){		
		float pas=(max-min)/(number-1);
		for (int i=1;i<number;i++){
			float freq = min+i*pas;
    			resul.add(freq);
		}
	}
	return resul;    
    }

    /**
     *
     * @return the set of frequencies the station can handle
     */
    public TreeSet getFrange(){
	return _frange;
    }

    /**
     *
     * @param the Wished set
     */
    public void setFrange(TreeSet t){
	_frange=t;
    }

    /***********************************Autres methodes******************************/

    /**
     * 
     */
    public void resetWishedEmission() {
	this.wishedEmission = new Vector();
		
    }
	
    public void resetWishedEmission3(){
	this.wishedEmission3=new Vector();
    }


    /**
     * 
     * @return a vector containing the obstacles that are in the cell where the station is.  
     */
    public Vector localize()
    {
	return simulator.getEnvironment().getCell(this.getX(), this.getY());
    }

    /**
     * Set the reach range to 1
     *
     */
    public void setMaxRange(){
	reachRate=1;
    }

    /**
     * Save current reach neighbors to former
     *
     */
    public void saveReachNeighbors(){
	formerReachNeighbors=currentReachNeighbors;
    }


    /**
     * Save current carrier neighbors to former
     *
     */
    public void saveCarrierNeighbors(){
	formerCarrierNeighbors=currentCarrierNeighbors;
    }

    /**
     * 
     * @param w the wished emission to add
     */
    public void addWishedEmission(WishedEmission w){
	wishedEmission.add(w);
    }
    /**
     * 
     * @param w the whished emission to remove
     */
    public void removeWishedEmission(WishedEmission w){
	wishedEmission.remove(w);
    }

    public void removeWishedEmission3(WishedEmission3 w){
	wishedEmission3.remove(w);
    }

    /**
     * 
     * @param w the ended emission to add
     */

    public void addEndedEmission(CurrentCommunication cc){
	endedEmission.add(cc);
    }
    /**
     * 
     * @param w the ended emission to remove
     */
    public void removeEndedEmission(WishedEmission w){
	endedEmission.remove(w);
    }

    /**
     * 
     * @param c the current communication to add
     */

    public void addCurrentCommunication(CurrentCommunication c){
	currentCommunication.add(c);
    }
    /**
     * 
     * @param c the current communication to remove
     */
    public void removeCurrentCommunication(CurrentCommunication c){
	currentCommunication.remove(c);
    }

    /**
     * USE FOR TEST
     * @return a string which describe the neighboorhood of this station
     */
    public String toStringNeighboorhood(){
	
	return  "Station Portee "+currentReachNeighbors.toString() +"\n Station porteuse "+currentCarrierNeighbors.toString()+"\n ------------------------\n" ;
	 
    }

    public Simulator getSimulator(){
	return simulator;
    }
    public String toString(){
	return name;
    }

    /**
     * 
     */
    public void saveNew2OldInterference() {
	oldInterferenced=newInterferenced;
	
    }

    /**
     * XML Identifier for a station
     * @see Results.Traceable#xmlId()
     */
    public String xmlId() {
	return _xmlKey;
    }

        
    /**
     *function to set the xml Identifier
     *
     *@params: String: name to set
     */
    public void setXMLId(String s){
	_xmlKey = s;
    }

    /**
     * XML representation of a station
     * @see Results.Dumpable#xmlDump()
     */
    public abstract String xmlDump();

    /**
     * @return the boolean calcul
     */
    public boolean isCalcul() {
	return calcul;
    }

    /**
     * 
     * @param b
     */
    public void setCalcul(boolean b) {
	calcul = b;
    }




    /**
     * builds a vector containing all the neighbors of the current station
     * located in an square shaped area around the station, which size
     * is twice the value of the given parameter
     * @param radius double
     * @return potential neighbors Vector<Station>
     */
    public Vector<Station> calculatePotentialNeighbors(double radius){

	Vector<Station> ngb = new Vector<Station>();
	int mode = simulator.getMode();

	if (this instanceof BaseStation) {
	    Vector<Station> stationList = simulator.getStation();
	    for (int i = 0; i < stationList.size(); i++) {
		Station stationTmp = stationList.get(i);

		if (stationTmp != this) {
		    if (!stationTmp.getAsleep()) {

			if ((getX() - radius <= stationTmp.getX())
			    && (stationTmp.getX() <= getX() + radius)
			    && ((getY() - radius <= stationTmp.getY()) && (stationTmp
									   .getY() <= getY() + radius)) ) {
			    //Verification of the compatibility between the baseStation frequencies and
			    //the stationTmp set of frequencies
			    if(getFmin()<stationTmp.getFmin() && 
			       getFmax()>stationTmp.getFmax()){
				ngb.add(stationTmp);
			    }
			}
		    } // asleep
		} // stationtmp!=s
	    } // for
	
	} else {
	    if (mode == Simulator.ADHOC || mode == Simulator.HYBRID || mode == Simulator.SENSOR) {
		Vector<MobileStation> stationList = simulator.getMobileStation();
		for (int i = 0; i < stationList.size(); i++) {
		    MobileStation stationTmp = stationList.get(i);
		    if (stationTmp != this) {
			if (!stationTmp.getAsleep()) {
			    if ((getX() - radius <= stationTmp.getX())
				&& (stationTmp.getX() <= getX() + radius)
				&& ((getY() - radius <= stationTmp.getY()) 
				    && (stationTmp.getY() <= getY() + radius))) {
				ngb.add(stationTmp);
			    }
			} // asleep
		    } // stationtmp!=s
		} // for
	    }
	
	    //CAS DU MODE HYBRIDE OU INFRASTRUCTURE (lles baseStation sont considérées comme des voisins potentiels)
	    if (mode == Simulator.INFRA || mode == Simulator.HYBRID) {
		Vector stationList = simulator.getBaseStation();
		for (int i = 0; i < stationList.size(); i++) {
		    BaseStation stationTmp = (BaseStation) stationList.get(i);

		    if (stationTmp != this) {
			if (!stationTmp.getAsleep()) {
			
			    if ((getX() - radius <= stationTmp.getX())
				&& (stationTmp.getX() <= getX()
				    + radius)
				&& ((getY() - radius <= stationTmp.getY()) 
				    && (stationTmp.getY() <= getY() + radius))) {
										
				if(getFmin()>stationTmp.getFmin() && 
				   getFmax()<stationTmp.getFmax()){
								
				    ngb.add(stationTmp);
				
				}		
			    }
			} // asleep
		    } // stationtmp!=s
		} // for
	    }
	}
	return ngb;

    }

  
}
